package pers.cz.orm.dao;

import pers.cz.common.connect.simpledb.DbConnnection;
import pers.cz.common.exception.ErrorCode;
import pers.cz.common.exception.OrmException;
import pers.cz.common.pub.AbstractEntity;
import pers.cz.common.pub.DbProcessor;
import pers.cz.jefconfig.tools.JefStringBuilder;
import pers.cz.orm.dao.processor.*;
import utils.JefLog;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * @program: jef-orm
 * @description: 公共dao
 * @author: Cheng Zhi
 * @create: 2023-08-10 10:12
 **/
public class CommonDbDaoImpl<T extends AbstractEntity> extends AbstractDbDao {

    private JefLog jefLog = new JefLog(CommonDbDaoImpl.class);

    @Override
    protected T load0(AbstractEntity entity) {

        DbProcessor dbProcessor = new SelectProcessor();
        dbProcessor.setTableMeta(initTableMeta(entity));
        dbProcessor.setEntity(entity);

        String sql = dbProcessor.getSql();

        return (T) sqlEngine.singleExecuteQuery(sql, entity.getClass());
    }

    @Override
    protected List<T> find0(AbstractEntity entity) {
        getDbListener().beforeQuery();
        DbProcessor dbProcessor = new SelectProcessor();
        dbProcessor.setTableMeta(initTableMeta(entity));
        dbProcessor.setShardingStrategy(shardingStrategy);
        dbProcessor.setEntity(entity);

        String sql = dbProcessor.getSql();
        getDbListener().afterQuery();
        return (List<T>) sqlEngine.executeQuery(sql, entity.getClass(), null);
    }

    @Override
    protected int insert0(AbstractEntity abstractEntity) {

        DbProcessor dbProcessor = new InsertProcessor();
        return singleExecSql(dbProcessor, abstractEntity, null);
    }

    @Override
    protected int override0(AbstractEntity abstractEntity) {
        DbProcessor dbProcessor = new OverrideProcessor();
        return singleExecSql(dbProcessor, abstractEntity, null);
    }

    // todo 未完整实现
    @Override
    protected int batchInsert0(List abstractEntitys) {

        BatchInsertProcessor batchInsertProcessor = new BatchInsertProcessor();
        batchInsertProcessor.setTableMeta(initTableMeta((AbstractEntity) abstractEntitys.get(0)));
        batchInsertProcessor.setShardingStrategy(shardingStrategy);
        batchInsertProcessor.setEntitys(abstractEntitys);

        return 0;
    }


    @Override
    protected int update0(AbstractEntity abstractEntity) {

        DbProcessor dbProcessor = new UpdateProcessor();
        return singleExecSql(dbProcessor, abstractEntity, null);
    }

    @Override
    protected int delete0(AbstractEntity abstractEntity) {
        DbProcessor dbProcessor = new DeleteProcessor();
        return singleExecSql(dbProcessor, abstractEntity, null);
    }

    @Override
    protected List selectAs0(String sql, Class resultType, Map map) {

        JefStringBuilder sb = new JefStringBuilder();
        boolean needJoin = false;
        if (map != null) {
            for (Map.Entry<Integer, Object> entry : (Set<Map.Entry<Integer, Object>>)map.entrySet()) {
                needJoin = true;
                sb.append("(");
                sb.append(entry.getKey());
                sb.append(")");
                sb.append(entry.getValue());
                sb.append(" ");
            }
        }

        if (needJoin) {
            sb.preAppand(" | [");
            sb.preAppand(sql);
            sb.append("]");
        } else {
            sb.append(sql);
        }
        jefLog.info("SHOW SQL: {}", sb.toString());
        return (List<T>) sqlEngine.executeQuery(sql, resultType, map);
    }

    @Override
    public List selectAs(String sql, Class resultType) {
        return selectAs(sql, resultType, null, null);
    }

    private int singleExecSql(DbProcessor dbProcessor, AbstractEntity abstractEntity, DataSource dataSource) {
        dbProcessor.setEntity(abstractEntity);
        dbProcessor.setShardingStrategy(shardingStrategy);
        dbProcessor.setTableMeta(initTableMeta(abstractEntity));
        String sql = dbProcessor.getSql();
        if (dataSource != null) {
            DbConnnection dbConnnection = new DbConnnection(dataSource);
            sqlEngine = new DefaultSqlEngine(dbConnnection);
        }
        return sqlEngine.execute(sql, null);
    }

    @Override
    public List find(AbstractEntity abstractEntity, DataSource dataSource) {

        DbProcessor dbProcessor = new SelectProcessor();
        dbProcessor.setShardingStrategy(shardingStrategy);
        dbProcessor.setTableMeta(initTableMeta(abstractEntity));
        dbProcessor.setEntity(abstractEntity);

        String sql = dbProcessor.getSql();
        try {
            return (List<T>) sqlEngine.executeQuery(sql, abstractEntity.getClass(), null);
        } catch (Exception e) {
            throw new OrmException(ErrorCode.ORM_USER_SINGLE_DATASOURCE_ERROR);
        }
    }

    @Override
    public int insert(AbstractEntity abstractEntity, DataSource dataSource) {
        DbProcessor dbProcessor = new InsertProcessor();
        try {
            return singleExecSql(dbProcessor, abstractEntity, dataSource);
        } catch (Exception e) {
            throw new OrmException(ErrorCode.ORM_USER_SINGLE_DATASOURCE_ERROR);
        }
    }

    @Override
    public int override(AbstractEntity abstractEntity, DataSource dataSource) {
        DbProcessor dbProcessor = new OverrideProcessor();
        try {
            return singleExecSql(dbProcessor, abstractEntity, dataSource);
        } catch (Exception e) {
            throw new OrmException(ErrorCode.ORM_USER_SINGLE_DATASOURCE_ERROR);
        }
    }

    @Override
    public int batchInsert(List abstractEntitys, DataSource dataSource) {
        BatchInsertProcessor batchInsertProcessor = new BatchInsertProcessor();
        batchInsertProcessor.setShardingStrategy(shardingStrategy);
        batchInsertProcessor.setTableMeta(initTableMeta((AbstractEntity) abstractEntitys.get(0)));
        batchInsertProcessor.setEntitys(abstractEntitys);
        return abstractEntitys.size();
    }

    @Override
    public int update(AbstractEntity abstractEntity, DataSource dataSource) {
        DbProcessor dbProcessor = new UpdateProcessor();
        try {
            return singleExecSql(dbProcessor, abstractEntity, dataSource);
        } catch (Exception e) {
            throw new OrmException(ErrorCode.ORM_USER_SINGLE_DATASOURCE_ERROR);
        }
    }

    @Override
    public int delete(AbstractEntity abstractEntity, DataSource dataSource) {
        DbProcessor dbProcessor = new DeleteProcessor();
        try {
            return singleExecSql(dbProcessor, abstractEntity, dataSource);
        } catch (Exception e) {
            throw new OrmException(ErrorCode.ORM_USER_SINGLE_DATASOURCE_ERROR);
        }
    }

    @Override
    public List selectAs(String sql, Class resultType, Map map, DataSource dataSource) {
        JefStringBuilder sb = new JefStringBuilder();
        boolean needJoin = false;
        if (map != null) {
            for (Map.Entry<Integer, Object> entry : (Set<Map.Entry<Integer, Object>>)map.entrySet()) {
                needJoin = true;
                sb.append("(");
                sb.append(entry.getKey());
                sb.append(")");
                sb.append(entry.getValue());
                sb.append(" ");
            }
        }

        if (needJoin) {
            sb.preAppand(" | [");
            sb.preAppand(sql);
            sb.append("]");
        } else {
            sb.append(sql);
        }
        jefLog.info("SHOW SQL: {}", sb.toString());
        try {
            DbConnnection dbConnnection = new DbConnnection(dataSource);
            sqlEngine = new DefaultSqlEngine(dbConnnection);
            return (List<T>) sqlEngine.executeQuery(sql, resultType, map);
        } catch (Exception e) {
            e.printStackTrace();
            throw new OrmException(ErrorCode.ORM_USER_SINGLE_DATASOURCE_ERROR);
        }
    }

    @Override
    public List selectAs(String sql, Class resultType, DataSource dataSource) {
        try {
            return selectAs(sql, resultType, null, dataSource);
        } catch (Exception e) {
            throw new OrmException(ErrorCode.ORM_USER_SINGLE_DATASOURCE_ERROR);
        }
    }
}
